/***********************************************************************************
 * 文件名： polsys.h
 * 版本： 4
 * 说明：
 * 		polsys 核心实现。 
 *      【polsys】
 *      使用软定时器原理的“多任务”系统, 轮询调用入口函数，非RTOS。支持在任意地方创建任务。
 *      【polsys_lite】
 *      通过在用户端提供宏定义配置的方式让用户添加几个固定间隔时间需要执行的任务函数，然后在
 *  Polsys_Loop 定时调用他们。 这样的机制可以在不支持函数指针的“低端RISC 8位单片机”中帮助代
 *  码去耦，让软件更具模块化和面向对象。
 *      【通用说明】
 *      polsys_port.c 文件中已经编写好移植示例，详见该文件内的注释说明。
 * 修改记录：
 * 	2021/04/28: 初版。 刘言。
 *  2021/05/19：合并polsys_lite。刘言。
 *  2021/11/15: 
 * 		定义 _POLSYS_DEBUG 后可以查看CPU使用率、是否过载等调试信息，每个TICK周期更新，过载
 * 	表示在一个TICK周期内不能执行完所有任务，一旦置位复位才能清除。
 * 		优化任务调度，现在只在有任务状态被标记为 TS_READY 后才会去扫描任务列表寻找并执行需要
 * 	执行的任务，其它时间都循环在执行 SYS_IDLE() 。刘言。
 * 	2022/11/28:
 * 		修复CPU使用率可能错误的BUG；
 * 		更改 IDLE 任务名为 Polsys_EventIdle；
 * 		新增优先级功能，间隔时间参数的高3位表示优先级，目前仅支持7优先级（实时任务，在中断中执行）。
 * 		依据 POLSYS_TICK_CYCLE 来设置任务运行间隔时间，API统一ms单位。
 * 		新增 Polsys_SetTaskReady 函数。
***********************************************************************************/
#ifndef _POLSYS_H_
#define _POLSYS_H_

#include <stddef.h>
#ifdef __NO_INCLUDE_PATH    // 如果有，定义在 stddef.h
#include "..\projects\prj\app\polsys_config.h"
#else
#include "polsys_config.h"
#endif
#include "polsys_mcu.h"

#ifdef __cplusplus
 extern "C" {
#endif

#ifdef POLSYS_LITE

// 仅为了伪函数优化曝光以下属性，用户不应该直接访问。

extern bit _Polsys_mTicked;
#ifdef _POLSYS_TASK_GROUP_1000TICK
extern u8 _Polsys_TickCount;
extern u8 _Polsys_10TickCount;
extern u8 _Polsys_100TickCount;
#elif (defined _POLSYS_TASK_GROUP_100TICK)
extern u8 _Polsys_TickCount;
extern u8 _Polsys_10TickCount;
#elif (defined _POLSYS_TASK_GROUP_10TICK)
extern u8 _Polsys_TickCount;
#endif

// 为了将 Polsys_Loop 函数定义成伪函数，才有了下面这一大推预处理

#if (defined _POLSYS_TASK_GROUP_10TICK)||(defined _POLSYS_TASK_GROUP_100TICK)||(defined _POLSYS_TASK_GROUP_1000TICK)
#ifdef _POLSYS_TASK_GROUP_10TICK
#define _Polsys_Do10TickTask()      _POLSYS_TASK_GROUP_10TICK
#else
#define _Polsys_Do10TickTask()
#endif
#if (defined _POLSYS_TASK_GROUP_100TICK)||(defined _POLSYS_TASK_GROUP_1000TICK)
#ifdef _POLSYS_TASK_GROUP_100TICK
#define _Polsys_Do100TickTask()      _POLSYS_TASK_GROUP_100TICK
#else
#define _Polsys_Do100TickTask()
#endif
#ifdef _POLSYS_TASK_GROUP_1000TICK
#define _Polsys_1000TickProcess()   \
{\
    _Polsys_100TickCount++;\
    if(_Polsys_100TickCount == 10)\
    {\
        _POLSYS_TASK_GROUP_1000TICK;\
        _Polsys_100TickCount = 0;\
    }\
}
#else
#define _Polsys_1000TickProcess()
#endif
#define _Polsys_100TickProcess()    \
{\
    _Polsys_10TickCount++;\
    if(_Polsys_10TickCount == 10)\
    {\
        _Polsys_Do100TickTask();\
        _Polsys_10TickCount = 0;\
        _Polsys_1000TickProcess();\
    }\
}
#else
#define _Polsys_100TickProcess()
#endif
#define _Polsys_10TickProcess() \
{\
    _Polsys_TickCount++;\
    if(_Polsys_TickCount == 10)\
    {\
        _Polsys_Do10TickTask();\
        _Polsys_TickCount = 0;\
        _Polsys_100TickProcess();\
    }\
}
#else
#define _Polsys_10TickProcess()
#endif


// APIs

#define Polsys_Init()   \
{\
    _Polsys_ClockInit();\
    _Polsys_TickStart();\
    Polsys_EnableInt();\
}

// 请在定时器中断中调用此函数
#define Polsys_Tick()   \
{   \
    _Polsys_mTicked = true;  \
}

// 在主循环中调用，用来执行各个任务
#define Polsys_Loop()   \
{\
    _POLSYS_TASK_GROUP_LOOP;\
    if (_Polsys_mTicked)\
    {\
        _Polsys_mTicked = false;\
        _POLSYS_TASK_GROUP_1TICK;\
        _Polsys_10TickProcess();\
    }\
}

#else

#if POLSYS_ENV_TYPE == 7
    #ifndef _POLSYS_USE_IDLE_CB
        #define _POLSYS_USE_IDLE_CB
    #endif
    /**
     * 设置 polsys 任务线程就绪去立即执行任务。
     * 
    */
    void Polsys_SetThreadReady();
#else
    #define Polsys_SetThreadReady()
#endif

//自定义数据类型

#define POLSYS_WAITING_FOREVER  0X1FFF  // 任务永远等待，将不会定时执行，必须手动控制

// 任务状态
typedef enum _task_status
{
	TS_WAIT = 0,    // 执行条件未到
	TS_READY,        // 执行条件已经到了，即将被执行
    TS_RUNNING      // 任务正在被执行
} task_status_t;

// 任务优先级
enum _task_prio
{
    TASK_PRIO_LOWEST = 0,
    TASK_PRIO_VERY_LOW = 1 << 13,
    TASK_PRIO_LOW = 2 << 13,
    TASK_PRIO_MIDDLE = 3 << 13,
    TASK_PRIO_HIGH = 4 << 13,
    TASK_PRIO_VERY_HIGH = 5 << 13,
    TASK_PRIO_HIGHEST = 6 << 13,
    TASK_PRIO_REAL_TIME = 7 << 13
};

#ifdef POLSYS_USE_PARAM
typedef void (*polsys_task_entry_t)(void *p);
#else
typedef void (*polsys_task_entry_t)(void);
#endif

// task “类”
typedef volatile struct _polsys_task
{
    // 入口函数
	polsys_task_entry_t EntryFunc;
#ifdef POLSYS_USE_PARAM
    void *Param;        // 入口函数的参数
#endif
    // 运行间隔时间（软定时周期）
	u16 Interval;
    // 还需要等待的时间
	u16 WaitTime;
    // 状态
	task_status_t Status;
#ifdef POLSYS_USE_PRIO
    u8 Prio;
#endif
#ifdef _POLSYS_DEBUG
    u8 CpuUsageAvg;		// 500ms内单个任务CPU平均使用率
    u8 CpuUsageMax;		// 3秒内单TICK周期最大的CPU使用率
    u8 CpuUsageLastTick;        // 一个TICK周期内单个任务的CPU占用率
	u16 CpuUsageAdd;	// 500ms内单个任务CPU累积使用率，用于计算平均使用率
    int RunClocks;	    // 一个TICK周期内执行单个任务消耗的定时器时钟周期数（执行耗时），用于计算CPU使用率
#endif
} polsys_task_t;


// 提供的接口

/**
 * 初始化 polsys。依次会执行 _Polsys_ClockInit _Polsys_TickStart 。
 */
void Polsys_Init();

void Polsys_Tick(); //在定时器中调用，更新计时
void Polsys_Loop(); //在主循环调用


#ifdef POLSYS_USE_PARAM
/**
 * 初始化一个带用户参数的任务，这个任务会被加入到任务列表。注意：同一个任务只能被初始化一次。
 * @param task_ptr 任务对象的指针，需要先静态定义一个task对象（polsys_task_t）
 * @param entry 任务入口函数
 * @param param 入口函数的参数
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
 * @return 初始化是否成功
 */
bool Polsys_InitTaskP(polsys_task_t *task_ptr, polsys_task_entry_t entry, void *param, u16 interval);
/**
 * 初始化一个任务，这个任务会被加入到任务列表，并立即开始运行。注意：同一个任务只能被初始化一次，否则会被重复加入列表重复运行。
 * @param task_ptr 任务对象的指针，需要先静态定义一个task对象（polsys_task_t）
 * @param entry 任务入口函数
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
 * @return 初始化是否成功
 */
STATIC_FORCE_IN_LINE bool Polsys_TaskInit(polsys_task_t *task_ptr, polsys_task_entry_t entry, u16 interval)
{
    return Polsys_InitTaskP(task_ptr, (polsys_task_entry_t)entry, NULL, interval);
}
/**
 * 初始化一个任务，这个任务会被加入到任务列表，并立即开始运行。注意：同一个任务只能被初始化一次，否则会被重复加入列表重复运行。
 * @param task_ptr 任务对象的指针，需要先静态定义一个task对象（polsys_task_t）
 * @param entry 任务入口函数
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
 * @return 初始化是否成功
 */
STATIC_FORCE_IN_LINE bool Polsys_InitTask(polsys_task_t *task_ptr, polsys_task_entry_t entry, u16 interval)
{
    return Polsys_TaskInit(task_ptr, entry, interval);
}
#else
/**
 * 初始化一个任务，这个任务会被加入到任务列表，并立即开始运行。注意：同一个任务只能被初始化一次，否则会被重复加入列表重复运行。
 * @param task_ptr 任务对象的指针，需要先静态定义一个task对象（polsys_task_t）
 * @param entry 任务入口函数
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
 * @return 初始化是否成功
 */
bool Polsys_TaskInit(polsys_task_t *task_ptr, polsys_task_entry_t entry, u16 interval);

/**
 * 初始化一个任务，这个任务会被加入到任务列表，并立即开始运行。注意：同一个任务只能被初始化一次，否则会被重复加入列表重复运行。
 * @param task_ptr 任务对象的指针，需要先静态定义一个task对象（polsys_task_t）
 * @param entry 任务入口函数
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
 * @return 初始化是否成功
 */
STATIC_FORCE_IN_LINE bool Polsys_InitTask(polsys_task_t *task_ptr, polsys_task_entry_t entry, u16 interval)
{
    return Polsys_TaskInit(task_ptr, entry, interval);
}
#endif

/**
 * 设置一个任务为 TS_READY 状态，其会不等待定时周期到来，尽快被执行。如果是实时任务，将在接下来的TICK中断中被执行。
 * 提示：使用RTOS时，如果在 Polsys_EventIdle 中阻塞了线程，需要实现 Polsys_SetThreadReady 唤醒执行 Polsys_Loop 的线程，以尽快执行 Ready 的任务，否则需要等待下一个TICK周期才会被执行。
 * @param task_ptr 一个 polsys_task_t 指针.
 */
void Polsys_SetTaskReady(polsys_task_t *task_ptr);

/**
 * 设置任务执行间隔时间
 * @param interval 执行间隔时间，单位ms，若处于 0 ~ POLSYS_TICK_CYCLE 之间将间
 * 隔 POLSYS_TICK_CYCLE 执行，若 = POLSYS_WAITING_FOREVER 将不会定时执行，仅能使用 
 * Polsys_SetTaskReady 手动控制其执行一次。
 * 高3位用于表示优先级，0为最低优先级，7表示实时任务
 * （在中断中被执行）， 建议用|运算符连接一个预定义的 _task_prio ，比如 10|TASK_PRIO_HIGH 
 * 表示间隔时间为 10 ms的高优先级任务。
*/
void Polsys_SetInterval(polsys_task_t *task_ptr, u16 interval);

#ifdef POLSYS_USE_PARAM
/**
 * 设置任务入口
 * @param entry 任务入口函数
 * @param param 入口函数的参数
*/
void Polsys_SetEntry(polsys_task_t *task_ptr, polsys_task_entry_t entry, void *param);
#else
/**
 * 设置任务入口
 * @param entry 任务入口函数
*/
void Polsys_SetEntry(polsys_task_t *task_ptr, polsys_task_entry_t entry);
#endif
// 动态创建一个任务。【待实现】
// polsys_task_t *Polsys_NewTask(polsys_task_entry_t entry, u16 interval);

// 动态删除一个任务，只能删除动态创建的任务。【待实现】
// Polsys_DeleteTask(polsys_task_t *task_ptr);
#endif

#ifdef __cplusplus
}
#endif

#include "polsys_cpts.h"

#endif
